/**
 * 此文件负责全部的socket逻辑
 * socket服务贯穿系统的使用周期（登录即建立连接->登出/离开即销毁连接）
 * 各个页面需要使用socket进行交互时，初始化时通过导出的单例注册监听事件
 *
 * @author wujianx
 * @createdate 2020-12-23
 */
import { JSONObject } from 'admin/schema/common';
import io from 'socket.io-client';
import store from 'store';
import { APP_ID } from 'utils';

/**
 * socket事件
 */
export declare type SocketEvent =
  /**任务状态通知 */
  | 'callTaskStatusBroadcastEvent'
  /** 任务状态通知 */
  | 'callTaskStatusEvent'
  /** 通话内容通知 */
  | 'dialogueContentEvent'
  /** 通话记录下载完成通知 */
  | 'callRecordDownloadCompleteEvent'
  /** 告警通知 */
  | 'alarmContentEvent'
  /** 流程调试通知 */
  | 'flowDebugEvent'
  /** 用户退登功能 */
  | 'userLogoutEvent';

export class SysWebSocket {
  /**
   * socket实例
   */
  public socket: SocketIOClient.Socket;

  /**
   * socket回调事件map
   */
  private eventMap: Map<SocketEvent, ((data: any) => any)[]>;
  /**
   * 存储socket消息Id（30s内）
   */
  private socketMsgMap: Map<any, any>;
  /**防止并发 */
  private updating: boolean;

  /** 私有构造函数 */
  constructor() {
    this.eventMap = new Map();
    this.socketMsgMap = new Map();
    this.updating = false;

    const appId = APP_ID;
    //获取域名（如果是ip地址，domain不带端口号）
    const host = window.location.host;

    let socket_url: any;
    if (!process.env.REACT_APP_SOCKET_URL?.startsWith('ws://')) {
      // 配置不正确，则取域名，拼接
      console.log(1);
      socket_url = 'ws://' + host;
      console.log('REACT_APP_SOCKET_URL--前端拼接>>>*****', socket_url);
    } else {
      // 配置有wss开头
      console.log(2);
      socket_url = process.env.REACT_APP_SOCKET_URL;
      console.log('REACT_APP_SOCKET_URL--部署配置>>>*****', socket_url);
    }

    // 配置local，则取域名，拼接
    // const socket_url_static = 'wss://' + host;
    // console.log('REACT_APP_SOCKET_URL--前端静态拼接>>>*****：', socket_url_static);

    const url = socket_url + '/' + appId;
    const userId = (store.getState() as JSONObject).userInfo?.userId;
    const token = (store.getState() as JSONObject).token;
    const socketUrl = `${url}?appId=${appId}&userId=${userId}&token=${token}&pushMode=0`;
    // 建立连接
    this.socket = io.connect(socketUrl, {
      transports: ['websocket'],
    });
    // 连接成功
    this.socket.on('connect', () => {
      console.log('ZNWH-V2 connected****:', socketUrl);
    });
    // 接收消息
    [
      'callTaskStatusBroadcastEvent',
      'callTaskStatusEvent',
      'dialogueContentEvent',
      'callRecordDownloadCompleteEvent',
      'alarmContentEvent',
      'flowDebugEvent',
      'userLogoutEvent',
    ].forEach(item => {
      this.socket.on(item, (data: JSONObject) => {
        const { msgId } = data;
        if (!msgId) {
          console.log('msgId为空', msgId, this.socketMsgMap);
          return;
        }
        // 1-向后端发送消息确认
        const jsonObject = {
          msgId: msgId,
          msgType: 'ACK',
        };
        // console.log('sendAckMessage msg:', jsonObject);
        this.socket.emit('AckEvent', jsonObject);

        // 更新存储数据
        // size大于1000执行清理
        if (this.socketMsgMap.size > 1000) {
          this.updateMsgIds();
        }

        // 2-去重思路
        // 客户端-我们可以通过构造一个map来维护已接收消息的id，当收到msgId重复的消息时直接丢弃
        const nowTime = Date.now(); // 当前时间
        if (this.socketMsgMap.has(msgId)) {
          // console.log('msgId重复***消息接收后忽略，不做处理', msgId, this.socketMsgMap);
          return;
        } else {
          this.socketMsgMap.set(msgId, nowTime);
          // console.log('msgId缓存***加入新数据*******size', this.socketMsgMap.size);
        }

        // 3-通知页面处理回调事件
        const events = this.eventMap.get(item as SocketEvent) || [];
        events.forEach(event => {
          event(data);
        });
      });
    });
  }

  /**
   * 添加socket事件
   */
  public addEvent(event: SocketEvent, func: (data: JSONObject) => any) {
    const events = this.eventMap.get(event) || [];
    !events.find(item => item.toString() === func.toString()) && events.push(func);
    this.eventMap.set(event, events);
  }
  /**
   * 删除socket事件
   */
  public removeEvent(eventArr: SocketEvent[]) {
    eventArr.forEach(item => {
      this.eventMap.set(item, []);
    });
  }

  /**
   * 通过socket向服务端推送数据
   */
  public send(event: string, data: JSONObject) {
    this.socket.emit(event, data);
  }

  /**
   * 销毁socket连接
   */
  public disposeConnect() {
    this.socket.close();
  }

  /**更新msgId存储数据
   * 去掉过期数据（前后端msgId有效期为30s，后台配置超过30秒的消息就不向前端推送了）
   */
  updateMsgIds() {
    if (this.updating) {
      console.log('updateMsgIds正在执行中，不能重复执行');
      return;
    }
    this.updating = true;
    const rangeTime = 30; // 有效期为30s
    const expiredArr: string[] = []; // 存储过期key:msgId
    const nowTime = Date.now(); // 当前时间

    this.socketMsgMap.forEach((value, key) => {
      const diffTime = (nowTime - value) / 1000; // 时间差的毫秒数
      const second = Math.floor(diffTime);
      if (second > rangeTime) {
        expiredArr.push(key);
      }
    });
    if (expiredArr.length) {
      console.log('有效期', rangeTime, 's,过期数组===expiredArr===', expiredArr, expiredArr.length);
      expiredArr.forEach((value, index) => {
        // 删除map中过期的键值对
        this.socketMsgMap.delete(value);
      });
    }
    this.updating = false;
  }
}

/**
 * 系统全局socket单例
 */
let socket: SysWebSocket;

/**
 * 初始化socket连接
 */
export const initSocket = (): void => {
  !socket && (socket = new SysWebSocket());
};

/**
 * 获取socket单例的唯一方式
 */
export const getSocket = (): SysWebSocket => {
  return socket || (socket = new SysWebSocket());
};
